# -*- coding: utf-8 -*-
"""
Created on Thu Sep 17 22:00:54 2020

@author: lenovo
"""

import pandas as pd
import numpy as np
import os
import inspect, re

'''
编写函数：
    将变量名转化为字符串
'''
def varname(p):
  for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
    m = re.search(r'\bvarname\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', line)
    if m:
      return m.group(1)

'''
编写函数：
    将dataframe存入excel中
'''
def QTDataIntoExcel(QTData,iteration):
    filename = os.path.join(r'Iteration',varname(QTData)+'_00'+str(iteration)+'.xlsx')
    QTData.to_excel(filename,sheet_name='sheet1',index=False)

def RsupIntoExcel(Rsup,iteration):
    filename = os.path.join(r'Iteration',varname(Rsup)+'_00'+str(iteration)+'.xlsx')
    Rsup.to_excel(filename,sheet_name='sheet1',index=False)
    
def RconfIntoExcel(Rconf,iteration):
    filename = os.path.join(r'Iteration',varname(Rconf)+'_00'+str(iteration)+'.xlsx')
    Rconf.to_excel(filename,sheet_name='sheet1',index=False)

def ScoreDataIntoExcel(ScoreData,iteration):
    filename = os.path.join(r'Iteration',varname(ScoreData)+'_00'+str(iteration)+'.xlsx')
    ScoreData.to_excel(filename,sheet_name='sheet1',index=False)

'''
编写函数：
    创建一个字典用于存放194个标签的编号。
    键---标签名称
    值---标签编码
'''
def labelEncode(rawdata):
    labelEncode_dict = {} #创建一个字典用于存放194个标签的编号
    for i in range(len(rawdata.axes[1])):
        labelEncode_dict[rawdata.axes[1][i]] = 't' + str(i)
    return labelEncode_dict           
          
'''
编写函数：
    输入：词篇矩阵QTData（DataFrame格式)
    输出：簇（dict格式）
    将词篇矩阵转化为新簇，键是标签编码，值是问答资源编码；
    并返回字典簇。
'''
def QTdataToDict(QTData):
    cu_dict = {} #创建一个字典存放新簇，键是标签，值是问答资源
    for index in QTData.columns:   #index为标签编码
        cu_dict[index] = []
        for que in QTData.index:  #que代表问答资源序号，由于问答资源不变，所以序号即编号
            if(QTData[index][que] == 1):
                cu_dict[index].append(que)
    return cu_dict

'''
编写函数：
    将新簇存放入txt文件中。
'''
def clusterIntoTxt(cu_dict,iteration):
    cu_File = open(os.path.join(r'Iteration',varname(cu_dict)+'_00'+str(iteration)+'.txt'),"a")
    for item in cu_dict.items():
        cu_File.write(str(item)+'\n')
    cu_File.close()

'''
编写函数：
    建立两个数据框，分别存放簇的支持度和置信度；
    列名为簇，行名为规则；
    矩阵中每一对数据表示的是：规则ti => tj(行 => 列)的支持度和置信度。
'''
def CaculateSupConf(cu_dict):
    Rsup = pd.DataFrame(columns=list(cu_dict.keys()),index=list(cu_dict.keys()))
    Rconf = pd.DataFrame(columns=list(cu_dict.keys()),index=list(cu_dict.keys()))
    for cu in cu_dict.keys():  #cu指对应的标签，即簇
        for rule in cu_dict.keys():   #rule指规则，即某个簇内包括的所有规则
            intersection = list(set(cu_dict[cu]) & set(cu_dict[rule]))
            Rsup[cu][rule] = len(intersection)/len(cu_dict[cu])
            Rconf[cu][rule] = len(intersection)/len(cu_dict[rule])
    return Rsup,Rconf

'''
编写函数：
    传入最小簇支持度CMinSup、簇支持度Rsup；
    由簇构造簇规则集;
    创建一个二维列表，第一维度是Rsup矩阵的列名，即簇名，第二维度是规则，即筛选的行。
'''
def clusterRule(CMinSup,Rsup):
    cuRule = {}
    for cu in Rsup.columns:   #cu表示簇，即Rsup中的列Rsup.columns
        cuRule[cu] = []
        for rule in Rsup.index: #rule表示规则，即Rsup中的行Rsup.index
            if(Rsup[cu][rule] > CMinSup):
                cuRule[cu].append(rule)  #针对每一列，把Rsup>0.1的行取出来
    return cuRule

'''
编写函数：
    建立一个字典，用于存放问题-标签数据；
    即每一个问题，对应有哪些标签
    参数：词篇数据QTData
'''
def QuestionLabelDict(QTData):
    QTDict = {}
    for que in QTData.index:  #que是行，即问答资源question
        QTDict[str(que)] = []
        for label in QTData.columns:  #label是列，即标签名label
            if(QTData[label][que] == 1):
                QTDict[str(que)].append(label)
    return QTDict

'''
编写函数：
    根据簇规则集重新聚类
        1、首先建立一个数据框，用于存放每个问题与各个簇的匹配得分。
        2、选定一个分配因子alpha（0-1之间），重新选出每一个问题属于哪些标签
        具体表示为：将每个问题对应的各个簇的得分的最大值与alpha的乘积作为阈值；
        大于阈值的则为该问题所属的新簇。
    传入的参数：
        问题-标签数据字典QTDict;
        分配因子alpha
'''
def Clustering(QTDict,Rconf,cuRule,alpha):
    #建立一个数据框，用于存放每个问题与各个簇的匹配得分，列名为簇标签，行名为问答资源
    ScoreData = pd.DataFrame(columns=Rconf.columns,index=list(QTDict.keys()))
    for que in QTDict.keys():
        for label in QTDict[que]:  
            #label对应QTDict中的标签，同时也是cuRule中的簇-键，而cuRule中的值对应的是Rconf中的行
            intersecLabel = list(set(cuRule[label]) & set(QTDict[que]))
            scoreT_Q = 0
            for x in intersecLabel:
                scoreT_Q = scoreT_Q + Rconf[label][x]
                                    #Rconf[label][x]表示label列x行
                ScoreData[label][que] = scoreT_Q
    ScoreData = ScoreData.fillna(value=0) #将空值全部用0替代
    #确定阈值，重新选出每一个问题属于哪些标签
    rowMax = ScoreData.max(axis=1) #将ScoreData中每行的最大值取出
    newQTData = pd.DataFrame(columns=ScoreData.columns,index=ScoreData.index)
    for que in ScoreData.index:
        for label in ScoreData.columns:
            threshold = alpha*rowMax[que]
            if(ScoreData[label][que] > threshold):
                newQTData[label][que] = 1
    newQTData = newQTData.fillna(value=0) #将空值全部用0替代
    return ScoreData,newQTData

'''
编写函数
    对新形成的词篇矩阵进行处理：
    1、将新的词篇矩阵转换成新的字典
    2、对字典进行删除空簇处理
    3、然后根据字典删除的簇，删除词篇数据对应的列
    4、对字典删除具有重复值的簇，保留一个
    5、相应的，删除词篇数据对应的列，保留一个
'''
def QTDataProcessing(newQTData):
    newCu = QTdataToDict(newQTData) #根据新的词篇数据转化为新的簇
    #删除空簇
    voidValue_key = []
    for item in newCu.items():
        if(item[1] == []):
            voidValue_key.append(item[0])
    for key in voidValue_key:
        del newCu[key]
        del newQTData[key]
    #删除字典中的有重复值的项，保留其中一个键，把重复值的键放入一个新的列表中
    dict_new={}
    for key,val in newCu.items():
        try:
            dict_new[str(val)]=key
        except TypeError as e:
            print("error:" + e)
    newCu_dict = {}
    for key,val in dict_new.items():
        try:
            newCu_dict[val] = list(map(int,eval(key)))
        except TypeError as e:
            print("error:" + e)
    #将词篇数据newQTData中删除相应的列，并将删除的
    for sameCu in list(set(newCu.keys()) - set(newCu_dict.keys())):
        del newQTData[sameCu]
    return newQTData,newCu_dict


'''
编写主函数
'''
def main():
    #从excel中读入数据，所读出的数据是DataFrame结构
    rawdata = pd.read_csv('词篇矩阵.csv',encoding = "utf-8")
    #data = rawdata.head(10) #返回rawdata前10行的数据
    labelEncode_dict = labelEncode(rawdata) #创建一个字典用于存放194个标签的编码
    print(labelEncode_dict)
    print('\n')
    rawdata.rename(columns = labelEncode_dict, inplace = True)
    QTData = rawdata
    cu_dict = QTdataToDict(QTData)
    newCu_dict = {}  #初始化一个新的字典用于存放每一次迭代形成的新簇
    iteration = 0 #用于计数迭代次数，从0开始，0--初始簇
    CMinSup = 0.0001 #赋值最小簇支持度
    alpha = 0.8 #赋值分配因子

    while (newCu_dict != cu_dict):   #判断新簇和上一步的簇是否相同，不相同则继续迭代
        if (newCu_dict != {}): 
            cu_dict = newCu_dict  #将新簇赋给cu_dict
        QTDataIntoExcel(QTData,iteration) #将每一步形成的问答-标签数据（0-1式）存入excel中
        clusterIntoTxt(cu_dict,iteration) #将每一步形成的簇存入txt文件中
        Rsup,Rconf = CaculateSupConf(cu_dict) #计算簇的支持度和置信度
        RsupIntoExcel(Rsup,iteration) #将每一步形成的簇支持度存入excel中
        RconfIntoExcel(Rconf,iteration) #将每一步形成的簇置信度存入excel中
        cuRule = clusterRule(CMinSup,Rsup) #根据最小簇支持度计算簇规则
        QTDict = QuestionLabelDict(QTData) #问题-标签编码映射字典
        ScoreData,newQTData = Clustering(QTDict,Rconf,cuRule,alpha) 
                        #根据簇规则集和分配因子重新聚类，返回得分数据和新的词篇数据
        ScoreDataIntoExcel(ScoreData,iteration)
        newQTData,newCu_dict = QTDataProcessing(newQTData) #经过删除处理后的新词篇数据和新簇
        iteration = iteration + 1  #迭代加1
        print("第",iteration,"次迭代")
        print('\n')
        QTData = newQTData #将新的词篇数据赋给QTData
    labelEncode_dict_exchange = dict(zip(labelEncode_dict.values(),
                                         labelEncode_dict.keys())) #键值对互换
    finalTag = dict([key,labelEncode_dict_exchange[key]] 
                        for key in cu_dict.keys()) #聚类后的最终标签
    print(finalTag)
A
#执行main函数
if __name__ == '__main__':
    main()